package com.bsi.config;

import com.google.common.collect.Lists;
import com.netflix.ribbon.proxy.annotation.Http;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.token.TokenService;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * @ClassName AuthorizationServerConfigurerAdapterEx
 * @Author LiuSong
 * @Date 2020/8/13 17:43
 * @Version 1.0
 * @Description
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigurerAdapterEx extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private DefaultTokenServices tokenServices;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    //AuthorizationServerSecurityConfigurer：用来配置令牌端点(Token Endpoint)的安全约束，在
    //AuthorizationServer中配置如下.
    //（1）tokenkey这个endpoint当使用JwtToken且使用非对称加密时，资源服务用于获取公钥而开放的，这里指这个
    //endpoint完全公开。
    //（2）checkToken这个endpoint完全公开
    //（3） 允许表单认证
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        return jwtAccessTokenConverter;
    }



    @Bean
    public DefaultTokenServices authorizationServerTokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//        defaultTokenServices.setTokenStore(new JdbcTokenStore(dataSource));
//        defaultTokenServices.setTokenEnhancer(new JwtAccessTokenConverter());
        defaultTokenServices.setTokenStore(tokenStore);
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        defaultTokenServices.setSupportRefreshToken(true);
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter()));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
        defaultTokenServices.setAccessTokenValiditySeconds(2000); // 令牌有效期
        defaultTokenServices.setRefreshTokenValiditySeconds(6000);// 刷新令牌的有效期
        return defaultTokenServices;
    }


    // 配置令牌存储的clientDetails
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        clients.withClientDetails(new JdbcClientDetailsService(dataSource));
        clients.inMemory().withClient("c1").secret(BCrypt.hashpw("secret", BCrypt.gensalt())).resourceIds("res1").
                // 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                        authorizedGrantTypes("authorization_code",
                        "password", "client_credentials", "implicit", "refresh_token")
                .scopes("all").autoApprove(false)//加上验证回调地址
                .redirectUris("http://www.baidu.com");

    }


    // AuthorizationServerEndpointsConfigurer 通过设定以下属性决定支持的授权类型（Grant Types）:
    //authenticationManager：认证管理器，当你选择了资源所有者密码（password）授权类型的时候，请设置
    //这个属性注入一个 AuthenticationManager 对象。
    //userDetailsService：如果你设置了这个属性的话，那说明你有一个自己的 UserDetailsService 接口的实现，
    //或者你可以把这个东西设置到全局域上面去（例如 GlobalAuthenticationManagerConfigurer 这个配置对
    //象），当你设置了这个之后，那么 "refresh_token" 即刷新令牌授权类型模式的流程中就会包含一个检查，用
    //来确保这个账号是否仍然有效，假如说你禁用了这个账户的话。
    //北京市昌平区建材城西路金燕龙办公楼一层 电话：400-618-9090
    //authorizationCodeServices：这个属性是用来设置授权码服务的（即 AuthorizationCodeServices 的实例对
    //象），主要用于 "authorization_code" 授权码类型模式。
    //implicitGrantService：这个属性用于设置隐式授权模式，用来管理隐式授权模式的状态。
    //tokenGranter：当你设置了这个东西（即 TokenGranter 接口实现），那么授权将会交由你来完全掌控，并
    //且会忽略掉上面的这几个属性，这个属性一般是用作拓展用途的，即标准的四种授权模式已经满足不了你的
    //需求的时候，才会考虑使用这个。
    // 配置授权端点的URL（Endpoint URLs）：
    //AuthorizationServerEndpointsConfigurer 这个配置对象有一个叫做 pathMapping() 的方法用来配置端点URL链
    //接，它有两个参数：
    //第一个参数：String 类型的，这个端点URL的默认链接。
    //第二个参数：String 类型的，你要进行替代的URL链接。
    //以上的参数都将以 "/" 字符为开始的字符串，框架的默认URL链接如下列表，可以作为这个 pathMapping() 方法的
    //第一个参数：
    ///oauth/authorize：授权端点。
    ///oauth/token：令牌端点。
    ///oauth/confirm_access：用户确认授权提交端点。
    ///oauth/error：授权服务错误信息端点。
    ///oauth/check_token：用于资源服务访问的令牌解析端点。
    ///oauth/token_key：提供公有密匙的端点，如果你使用JWT令牌的话。
    //需要注意的是授权端点这个URL应该被Spring Security保护起来只供授权用户访问
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenServices(tokenServices)
                .authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices)
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }

}
